﻿
Option Strict On

Imports System.IO
Imports osi.root.constants
Imports osi.root.formation
Imports osi.root.lock
Imports osi.root.procedure
Imports osi.root.utils
Imports osi.service.argument
Imports osi.service.convertor
Imports osi.service.device
Imports osi.service.commander.constants

<global_init(global_init_level.server_services)>
Public Class questioner
    Private ReadOnly instance As herald
    Private ReadOnly creator As Func(Of herald)
    Private ReadOnly pool As idevice_pool(Of herald)
    Private ReadOnly name As String
    Private ReadOnly wait_device_timeout_ms As Int64
    Private ReadOnly timeout_ms As Int64
    Private ReadOnly last_error As atomic_int

    Private Sub New(ByVal instance As herald,
                    ByVal creator As Func(Of herald),
                    ByVal pool As idevice_pool(Of herald),
                    ByVal name As String,
                    ByVal wait_device_timeout_ms As Int64,
                    ByVal timeout_ms As Int64)
        Me.instance = instance
        Me.creator = creator
        Me.pool = pool
        Me.name = name
        Me.wait_device_timeout_ms = wait_device_timeout_ms
        Me.timeout_ms = timeout_ms
        Me.last_error = New atomic_int()
    End Sub

    Public Sub New(ByVal instance As herald, ByVal timeout_ms As Int64)
        Me.New(instance, Nothing, Nothing, Nothing, npos, timeout_ms)
    End Sub

    Public Sub New(ByVal creator As Func(Of herald), ByVal timeout_ms As Int64)
        Me.New(Nothing, creator, Nothing, Nothing, npos, timeout_ms)
    End Sub

    Public Sub New(ByVal pool As idevice_pool(Of herald),
                   ByVal wait_device_timeout_ms As Int64,
                   ByVal timeout_ms As Int64)
        Me.New(Nothing, Nothing, pool, Nothing, wait_device_timeout_ms, timeout_ms)
    End Sub

    Public Sub New(ByVal name As String,
                   ByVal wait_device_timeout_ms As Int64,
                   ByVal timeout_ms As Int64)
        Me.New(Nothing, Nothing, Nothing, name, wait_device_timeout_ms, timeout_ms)
    End Sub

    Public Function valid() As Boolean
        Return Not instance Is Nothing OrElse
               Not creator Is Nothing OrElse
               Not pool Is Nothing OrElse
               Not String.IsNullOrEmpty(name)
    End Function

    Private Function query(ByVal i As command,
                           ByVal o As pointer(Of command),
                           ByVal h As herald) As event_comb
        assert(Not h Is Nothing)
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  If i Is Nothing OrElse h Is Nothing Then
                                      Return False
                                  Else
                                      ec = h.send(i)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  End If
                              End Function,
                              Function() As Boolean
                                  assert(Not h Is Nothing)
                                  If ec.end_result() Then
                                      ec = h.sense(timeout_ms)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  assert(Not h Is Nothing)
                                  If ec.end_result() Then
                                      ec = h.receive(o)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Private Function query_by_instance(ByVal i As command,
                                       ByVal o As pointer(Of command)) As event_comb
        assert(Not instance Is Nothing)
        Return query(i, o, instance)
    End Function

    Private Function query_by_creator(ByVal i As command,
                                      ByVal o As pointer(Of command)) As event_comb
        assert(Not creator Is Nothing)
        Dim q As herald = Nothing
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  q = creator()
                                  If q Is Nothing Then
                                      Return False
                                  Else
                                      ec = query(i, o, q)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Private Function query_by_pool(ByVal i As command,
                                   ByVal o As pointer(Of command),
                                   ByVal pool As idevice_pool(Of herald)) As event_comb
        assert(Not pool Is Nothing)
        Dim ec As event_comb = Nothing
        Dim q As pointer(Of herald) = Nothing
        Return New event_comb(Function() As Boolean
                                  q = New pointer(Of herald)()
                                  ec = pool.get(q, wait_device_timeout_ms)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() AndAlso
                                     Not (+q) Is Nothing Then
                                      ec = query(i, o, +q)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  assert(Not pool Is Nothing)
                                  pool.release(+q)
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Private Function query_by_pool(ByVal i As command,
                                   ByVal o As pointer(Of command)) As event_comb
        Return query_by_pool(i, o, pool)
    End Function

    Private Function query_by_name(ByVal i As command,
                                   ByVal o As pointer(Of command)) As event_comb
        assert(Not String.IsNullOrEmpty(name))
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  Dim r As idevice_pool(Of herald) = Nothing
                                  If device_manager.get(name, r) Then
                                      ec = query_by_pool(i, o, r)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Private Function query(ByVal i As command,
                           ByVal o As pointer(Of command)) As event_comb
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  If Not instance Is Nothing Then
                                      ec = query_by_instance(i, o)
                                  ElseIf Not creator Is Nothing Then
                                      ec = query_by_creator(i, o)
                                  ElseIf Not pool Is Nothing Then
                                      ec = query_by_pool(i, o)
                                  ElseIf Not String.IsNullOrEmpty(name) Then
                                      ec = query_by_name(i, o)
                                  Else
                                      Return assert(False)
                                  End If
                                  assert(Not ec Is Nothing)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Default Public ReadOnly Property question(ByVal request As command,
                                              ByVal response As pointer(Of command)) As event_comb
        Get
            Dim ec As event_comb = Nothing
            Return New event_comb(Function() As Boolean
                                      If valid() AndAlso
                                         (+last_error) < last_error_count Then
                                          ec = query(request, response)
                                          Return waitfor(ec) AndAlso
                                                 goto_next()
                                      Else
                                          Return False
                                      End If
                                  End Function,
                                  Function() As Boolean
                                      If ec.end_result() Then
                                          Return goto_end()
                                      Else
                                          If last_error.increment() = last_error_count Then
                                              start_ping(make_weak_pointer(Me))
                                          End If
                                          Return False
                                      End If
                                  End Function)
        End Get
    End Property

    Default Public ReadOnly Property question(ByVal request() As Byte,
                                              ByVal response As pointer(Of Byte())) As event_comb
        Get
            Dim r As pointer(Of command) = Nothing
            Dim ec As event_comb = Nothing
            Return New event_comb(Function() As Boolean
                                      r = New pointer(Of command)()
                                      ec = question(command.[New](request), r)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  End Function,
                                  Function() As Boolean
                                      Return ec.end_result() AndAlso
                                             Not (+r) Is Nothing AndAlso
                                             eva(response, (+r).action()) AndAlso
                                             goto_end()
                                  End Function)
        End Get
    End Property

    Default Public ReadOnly Property question(ByVal request As command,
                                              ByVal response As Func(Of command, Boolean)) As event_comb
        Get
            Dim r As pointer(Of command) = Nothing
            Dim ec As event_comb = Nothing
            Return New event_comb(Function() As Boolean
                                      r = New pointer(Of command)()
                                      ec = question(request, r)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  End Function,
                                  Function() As Boolean
                                      Return ec.end_result() AndAlso
                                             Not (+r) Is Nothing AndAlso
                                             (+r).action_is(constants.response.success) AndAlso
                                             (response Is Nothing OrElse response(+r)) AndAlso
                                             goto_end()
                                  End Function)
        End Get
    End Property

    Default Public ReadOnly Property question(ByVal request As command,
                                              ByVal response As Func(Of Boolean)) As event_comb
        Get
            Return question(request,
                            Function(c As command) As Boolean
                                Return response Is Nothing OrElse response()
                            End Function)
        End Get
    End Property

    Default Public ReadOnly Property question(ByVal request As command) As event_comb
        Get
            Return question(request, Function() True)
        End Get
    End Property

    Private Function ping_once() As event_comb
        Dim ec As event_comb = Nothing
        Dim o As pointer(Of command) = Nothing
        Return New event_comb(Function() As Boolean
                                  assert(valid())
                                  o = New pointer(Of command)()
                                  ec = query(command.[New](action.ping), o)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         Not (+o) Is Nothing AndAlso
                                         (+o).action_is(response.success) AndAlso
                                         goto_end()
                              End Function)
    End Function

    Private Shared Sub start_ping(ByVal p As weak_pointer(Of questioner))
        Dim ec As event_comb = Nothing
        assert_begin(New event_comb(Function() As Boolean
                                        Dim q As questioner = Nothing
                                        q = (+p)
                                        If q Is Nothing Then
                                            Return goto_end()
                                        Else
                                            ec = q.ping_once()
                                            Return waitfor(ec) AndAlso
                                                   goto_next()
                                        End If
                                    End Function,
                                    Function() As Boolean
                                        Dim q As questioner = Nothing
                                        q = (+p)
                                        If q Is Nothing Then
                                            Return goto_end()
                                        Else
                                            If ec.end_result() Then
                                                q.last_error.set(0)
                                                Return goto_end()
                                            Else
                                                Return waitfor(ping_interval_ms) AndAlso
                                                       goto_begin()
                                            End If
                                        End If
                                    End Function))
    End Sub

    Public Shared Function create(ByVal v As var, ByVal o As pointer(Of questioner)) As event_comb
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  If v Is Nothing Then
                                      Return False
                                  Else
                                      Dim wait_device_timeout_ms As Int64 = 0
                                      Dim timeout_ms As Int64 = 0
                                      wait_device_timeout_ms = v("wait-device-timeout-ms").to_int64(default_value:=npos)
                                      timeout_ms = v("timeout-ms").to_int64(default_value:=npos)
                                      Dim name As String = Nothing
                                      If v.value("name", name) Then
                                          Return eva(o,
                                                     New questioner(name, wait_device_timeout_ms, timeout_ms)) AndAlso
                                                 goto_end()
                                      Else
                                          ec = secondary_resolve(v,
                                                                 constants.herald_secondary_type_name,
                                                                 Function(i As idevice_pool(Of herald),
                                                                        ByRef r As questioner) As Boolean
                                                                     r = New questioner(i,
                                                                                        wait_device_timeout_ms,
                                                                                        timeout_ms)
                                                                     Return True
                                                                 End Function,
                                                                 o)
                                          Return waitfor(ec) AndAlso
                                                 goto_next()
                                      End If
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Private Shared Sub init()
        device_constructor(Of questioner).set_create_singleton_device_pool_binder(
            Function(x As questioner, ByRef o As singleton_device_pool(Of questioner)) As Boolean
                o = New singleton_device_pool(Of questioner)(x)
                Return True
            End Function)
        assert(device_constructor.register(Of questioner) _
                                          (Function(v As var, o As pointer(Of questioner)) As event_comb
                                               Return create(v, o)
                                           End Function))
    End Sub
End Class
